home *** CD-ROM | disk | FTP | other *** search
Text File | 1997-05-07 | 18.5 KB | 550 lines | [TEXT/CWIE] |
- unit MyWindows;
-
- { Based heavilly on Dean Yu's Develop #17 code }
-
- interface
-
- uses
- Types, Windows, Quickdraw, MyAssertions;
-
- const
- document_titlebar_height = 18;
-
- {$ifc not do_debug}
- {$definec AssertValidWindow(w)}
- {$elsec}
- {$definec AssertValidWindow(w) AssertValidWindowCode(w)}
- {$endc}
-
- {$ifc do_debug}
- procedure AssertValidWindowCode(window: WindowPtr);
- {$endc}
-
- procedure ZoomTheWindow (window: WindowPtr; zoomout: boolean; idealsize: Point; var unzoomed: Rect);
- procedure ZoomWindowOut (window: WindowPtr; titlebar_height:integer; idealsize: Point);
- procedure GetWindowRect (window: WindowPtr; var r: Rect);
- procedure SetWindowRect (window: WindowPtr; const r: Rect);
- procedure GetWindowPosition(window:WindowPtr; var pos:Point);
- procedure SetWindowPosition(window:WindowPtr; pos:Point);
- function GetWindowContentRegion (window: WindowPtr): RgnHandle;
- function GetWindowStructureRegion (window: WindowPtr): RgnHandle;
- procedure GetWindowPortRect (window: WindowPtr; var portRect: Rect);
- function GetWindowVisible (window: WindowPtr): boolean;
- procedure GetWindowStandardState (window: WindowPtr; var standardState: Rect);
- procedure SetWindowStandardState (window: WindowPtr; standardState: Rect);
- procedure GetWindowUserState (window: WindowPtr; var userState: Rect);
- procedure SetWindowUserState (window: WindowPtr; userState: Rect);
- function TitleBarOnScreen (window: WindowPtr): boolean;
- procedure GetBestScreenRect(windowBounds: Rect; var screenRect:Rect; var on_main_device:boolean);
- function IsWindowShaded(window: WindowPtr): boolean;
- function WindowInWindowList (window: WindowPtr): boolean;
- procedure StaggerWindow( window: WindowPtr );
- function FrontWindowList: WindowPtr;
- function NextWindowList( window: WindowPtr ): WindowPtr;
- procedure CenterWindow( window: WindowPtr; center: Rect );
-
- implementation
-
- { Based on code by Dean Yu in Develop 17 }
- { Changes: }
- { Converted to Pascal }
- { Pass in desired window size instead of a ProcPtr to return the desired window size }
- { Removed use of DeviceLoop }
- { (DeviceLoop is System 7 dependent, and doesn't work in THINK Pascal anyway due to a bug in the interfaces }
- { Improved to Handle zooming windows before they are made visible (since struct and content rgn's are empty) }
-
- uses
- Script, LowMem,
- MyTypes, MySystemGlobals, MyMathUtils, MyAssertions,
- MyUtils; { delete MyUtils }
-
- const
- kNudgeSlop = 4;
- kIconSpace = 64;
-
- {$ifc undefined allow_windows_not_in_window_list}
- {$setc allow_windows_not_in_window_list := false}
- {$endc}
-
- { WindowRecord accessor functions }
-
- {$ifc do_debug}
- procedure AssertValidWindowCode(window: WindowPtr);
- {$ifc not allow_windows_not_in_window_list}
- var
- nw: WindowPtr;
- {$endc}
- begin
- Assert( window <> nil );
- {$ifc not allow_windows_not_in_window_list}
- nw := FrontWindowList;
- while (nw <> nil) & (window <> nw) do begin
- nw := WindowPtr( WindowPeek(nw)^.nextWindow );
- end;
- Assert( nw <> nil );
- {$endc}
- end;
- {$endc}
-
- function GetWindowContentRegion (window: WindowPtr): RgnHandle;
- begin
- AssertValidWindow( window );
- GetWindowContentRegion := WindowPeek(window)^.contRgn;
- end;
-
- function GetWindowStructureRegion (window: WindowPtr): RgnHandle;
- begin
- AssertValidWindow( window );
- GetWindowStructureRegion := WindowPeek(window)^.strucRgn;
- end;
-
- procedure GetWindowPortRect (window: WindowPtr; var portRect: Rect);
- begin
- AssertValidWindow( window );
- portRect := WindowPeek(window)^.port.portRect;
- end;
-
- function GetWindowVisible (window: WindowPtr): boolean;
- begin
- AssertValidWindow( window );
- GetWindowVisible := WindowPeek(window)^.visible;
- end;
-
- procedure GetWindowStandardState (window: WindowPtr; var standardState: Rect);
- begin
- AssertValidWindow( window );
- Assert( WindowPeek(window)^.dataHandle <> nil );
- standardState := WStateDataHandle(WindowPeek(window)^.dataHandle)^^.stdState;
- end;
-
- procedure SetWindowStandardState (window: WindowPtr; standardState: Rect);
- begin
- AssertValidWindow( window );
- Assert( WindowPeek(window)^.dataHandle <> nil );
- WStateDataHandle(WindowPeek(window)^.dataHandle)^^.stdState := standardState;
- end;
-
- procedure GetWindowUserState (window: WindowPtr; var userState: Rect);
- begin
- AssertValidWindow( window );
- Assert( WindowPeek(window)^.dataHandle <> nil );
- userState := WStateDataHandle(WindowPeek(window)^.dataHandle)^^.userState;
- end;
-
- procedure SetWindowUserState (window: WindowPtr; userState: Rect);
- begin
- AssertValidWindow( window );
- Assert( WindowPeek(window)^.dataHandle <> nil );
- WStateDataHandle(WindowPeek(window)^.dataHandle)^^.userState := userState;
- end;
-
- procedure GetWindowRect (window: WindowPtr; var r: Rect);
- begin
- AssertValidWindow( window );
- SetPort(window);
- GetWindowPortRect(window, r);
- LocalToGlobal(r.topLeft);
- LocalToGlobal(r.botRight);
- end;
-
- procedure SetWindowRect (window: WindowPtr; const r: Rect);
- begin
- AssertValidWindow( window );
- MoveWindow(window,r.left,r.top,false);
- SizeWindow(window,r.right-r.left,r.bottom-r.top,true);
- end;
-
- procedure GetWindowPosition(window:WindowPtr; var pos:Point);
- var
- r:Rect;
- begin
- AssertValidWindow( window );
- SetPort(window);
- GetWindowPortRect(window, r);
- pos:=r.topLeft;
- LocalToGlobal(pos);
- end;
-
- procedure SetWindowPosition(window:WindowPtr; pos:Point);
- var
- r:Rect;
- begin
- AssertValidWindow( window );
- SetPort(window);
- GetWindowPortRect(window, r);
- OffsetRect(r,-r.left+pos.h,-r.top+pos.v);
- SetWindowRect(window,r);
- end;
-
- function GetBestDevice (windowBounds: Rect): GDHandle;
- var
- thisGD, bestGD: GDHandle;
- thisArea, bestArea: longint;
- thisBounds: Rect;
- dummy: boolean;
- begin
- thisGD := GetDeviceList;
- bestArea := 0;
- bestGD := GetMainDevice;
- while thisGD <> nil do begin
- if TestDeviceAttribute(thisGD, screenDevice) & TestDeviceAttribute(thisGD, screenActive) then begin
- dummy := SectRect(windowBounds, thisGD^^.gdRect, thisBounds);
- thisArea := longint(thisBounds.right - thisBounds.left) * longint(thisBounds.bottom - thisBounds.top);
- if thisArea > bestArea then begin
- bestGD := thisGD;
- bestArea := thisArea;
- end;
- end;
- thisGD := GetNextDevice(thisGD);
- end;
- GetBestDevice := bestGD;
- end;
-
- procedure GetBestScreenRect(windowBounds: Rect; var screenRect:Rect; var on_main_device:boolean);
- { NOTE: screenRect will not include the menu bar }
- var
- screenWithLargestPartOfWindow: GDHandle;
- begin
- if has_ColourQuickDraw then begin
- screenWithLargestPartOfWindow := GetBestDevice(windowBounds);
- screenRect := screenWithLargestPartOfWindow^^.gdRect;
- on_main_device := GetMainDevice = screenWithLargestPartOfWindow;
- end else begin
- screenRect := GetQDGlobals^.screenBits.bounds;
- on_main_device := true;
- end;
- if on_main_device then begin
- screenRect.top := screenRect.top + LMGetMBarHeight;
- end;
- end;
-
- { Figure out how much we need to move the window to get it entirely on the monitor. If }
- { the window wouldn’t fit completely on the monitor anyway, don’t move it at all; we’ll }
- { make it fit later on. }
-
- function CalculateOffsetAmount (idealStartPoint, idealEndPoint, idealOnScreenStartPoint, idealOnScreenEndPoint, screenEdge1, screenEdge2: integer): integer;
- var
- offsetAmount: integer;
- begin
- { First check to see if the window fits on the screen in this dimension. }
- if (idealStartPoint < screenEdge1) & (idealEndPoint > screenEdge2) then begin
- offsetAmount := 0;
- end else begin
-
- { Find out how much of the window lies off this screen by subtracting the amount of the window }
- { that is on the screen from the size of the entire window in this dimension. If the window }
- { is completely offscreen, the offset amount is going to be the distance from the ideal }
- { starting Point to the first edge of the screen. }
- if idealOnScreenStartPoint - idealOnScreenEndPoint = 0 then begin
- { See if the window is lying to the left or above the screen }
- if idealEndPoint < screenEdge1 then begin
- offsetAmount := screenEdge1 - idealStartPoint + kNudgeSlop;
- end else begin
- { Otherwise, it’s below or to the right of the screen }
- offsetAmount := screenEdge2 - idealEndPoint - kNudgeSlop;
- end;
- end else begin
- { Window is already partially or completely on the screen }
- offsetAmount := (idealEndPoint - idealStartPoint) - (idealOnScreenEndPoint - idealOnScreenStartPoint);
-
- { If we are offscreen a little, move the window in a few more pixels from the edge of the screen. }
- if offsetAmount <> 0 then begin
- offsetAmount := offsetAmount + kNudgeSlop;
- end;
-
- { Check to see which side of the screen the window was falling off of, so that it can be }
- { nudged in the opposite direction. }
- if idealEndPoint > screenEdge2 then begin
- offsetAmount := -offsetAmount;
- end;
- end;
- end;
-
- CalculateOffsetAmount := offsetAmount;
- end;
-
- procedure AddRect (r1, r2: Rect; var r: Rect);
- begin
- r.top := r1.top + r2.top;
- r.bottom := r1.bottom + r2.bottom;
- r.left := r1.left + r2.left;
- r.right := r1.right + r2.right;
- end;
-
- procedure SubRect (r1, r2: Rect; var r: Rect);
- begin
- r.top := r1.top - r2.top;
- r.bottom := r1.bottom - r2.bottom;
- r.left := r1.left - r2.left;
- r.right := r1.right - r2.right;
- end;
-
- procedure ZoomWindowOut (window: WindowPtr; titlebar_height:integer; idealsize: Point);
- var
- windowBounds: Rect;
- newStandardRect: Rect;
- scratchRect: Rect;
- screenRect: Rect;
- portRect: Rect;
- contentRegionBoundingBox: Rect;
- structureRegionBoundingBox: Rect;
- scratchRegion: RgnHandle;
- structureRegion: RgnHandle;
- contentRegion: RgnHandle;
- on_main_device: boolean;
- horizontalAmountOffScreen: integer;
- verticalAmountOffScreen: integer;
- windowFrame: Rect;
- dummy: boolean;
- orgrect: Rect;
- begin
- AssertValidWindow( window );
- SetPort(window);
-
- GetWindowRect(window, orgrect);
-
- contentRegion := GetWindowContentRegion(window);
- structureRegion := GetWindowStructureRegion(window);
- GetWindowPortRect(window, portRect);
-
- { If the window is invisible (or at least initially before it is ever made visible), then the content and structure }
- { regions will be empty. In this case, we fake it out by using the portRect as the content region and 18 (hardcoded) }
- { as the titlebar height }
- if EmptyRgn(structureRegion) then begin
- GetWindowRect(window, scratchRect);
- contentRegionBoundingBox := scratchRect;
- scratchRect.top := scratchRect.top - titlebar_height; { No other way of figuring out the window frame }
- structureRegionBoundingBox := scratchRect;
- end else begin
- contentRegionBoundingBox := contentRegion^^.rgnBBox;
- structureRegionBoundingBox := structureRegion^^.rgnBBox;
- end;
-
- { Determine the size of the window frame }
- windowFrame.top := structureRegionBoundingBox.top - contentRegionBoundingBox.top;
- windowFrame.left := structureRegionBoundingBox.left - contentRegionBoundingBox.left;
- windowFrame.right := structureRegionBoundingBox.right - contentRegionBoundingBox.right;
- windowFrame.bottom := structureRegionBoundingBox.bottom - contentRegionBoundingBox.bottom;
-
- { If the window is being zoomed into the standard state, calculate the best size }
- { to display the window’s information. }
- { Usually, we would use the content region’s bounding box to determine the monitor }
- { with largest portion of the window’s area. However, if the entire content region }
- { of the window is not on any screen, the structure region should be used instead. }
- windowBounds := contentRegionBoundingBox;
- scratchRegion := NewRgn;
- RectRgn(scratchRegion, windowBounds);
- SectRgn(GetGrayRgn, scratchRegion, scratchRegion);
- if EmptyRgn(scratchRegion) then begin
- windowBounds := structureRegionBoundingBox;
- end;
- DisposeRgn(scratchRegion);
-
- GetBestScreenRect(windowBounds,screenRect,on_main_device);
-
-
- { Go figure out the perfect size for the window as if we had an infinitely large }
- { screen }
- { (*calcRoutine)((WindowPtr) window, &newStandardRect);}
- SetRect(newStandardRect, 0, 0, idealsize.h, idealsize.v);
-
- { Anchor the new rectangle at the window’s current top left corner }
- { OffsetRect(&newStandardRect, -newStandardRect.left, -newStandardRect.top); }
- OffsetRect(newStandardRect, orgrect.left, orgrect.top);
-
- { newStandardRect is the ideal size for the content area. The window frame }
- { needs to be accounted for when we see if the window needs to be moved, }
- { or resized, so add in the dimensions of the window frame.}
- AddRect(newStandardRect, windowFrame, newStandardRect);
-
- { { If the new rectangle falls off the edge of the screen, nudge it so that it’s just }
- { on the screen. CalculateOffsetAmount determines how much of the window is offscreen. }
- dummy := SectRect(newStandardRect, screenRect, scratchRect);
- if not EqualRect(newStandardRect, scratchRect) then begin
- horizontalAmountOffScreen := CalculateOffsetAmount(newStandardRect.left, newStandardRect.right, scratchRect.left, scratchRect.right, screenRect.left, screenRect.right);
- verticalAmountOffScreen := CalculateOffsetAmount(newStandardRect.top, newStandardRect.bottom, scratchRect.top, scratchRect.bottom, screenRect.top, screenRect.bottom);
- OffsetRect(newStandardRect, horizontalAmountOffScreen, verticalAmountOffScreen);
- end;
-
- { If we’re still falling off the edge of the screen, that means that the perfect }
- { size is larger than the screen, so we need to shrink down the standard size }
- dummy := SectRect(newStandardRect, screenRect, scratchRect);
- if not EqualRect(newStandardRect, scratchRect) then begin
-
- { First shrink the width of the window. If the window is wider than the screen }
- { it is zooming to, we can just pin the standard rectangle to the edges of the }
- { screen, leaving some slop. If the window is narrower than the screen, we know }
- { we just nudged it into position, so nothing needs to be done. }
- if newStandardRect.right - newStandardRect.left > screenRect.right - screenRect.left then begin
- newStandardRect.left := screenRect.left + kNudgeSlop;
-
- if (on_main_device) then begin
- newStandardRect.right := screenRect.right - kIconSpace;
- end else begin
- newStandardRect.right := screenRect.right - kNudgeSlop;
- end;
- end;
-
- { Move in the top. Like the width of the window, nothing needs to be done unless }
- { the window is taller than the height of the screen. }
- if newStandardRect.bottom - newStandardRect.top > screenRect.bottom - screenRect.top then begin
- newStandardRect.top := screenRect.top + kNudgeSlop;
- newStandardRect.bottom := screenRect.bottom - kNudgeSlop;
- end;
- end;
-
- { We’ve got the best possible window position. Remove the }
- { frame, slam it into the WStateData record and let ZoomWindow }
- { take care of the rest. }
- SubRect(newStandardRect, windowFrame, newStandardRect);
-
- if (newStandardRect.left = orgrect.left) & (newStandardRect.top = orgrect.top) then begin
- SizeWindow(window, newStandardRect.right - newStandardRect.left, newStandardRect.bottom - newStandardRect.top, true);
- end else begin
- SetWindowRect(window, newStandardRect);
- end;
- { If the window is still anchored at the current location, then just resize it }
- end;
-
- procedure ZoomTheWindow (window: WindowPtr; zoomout: boolean; idealsize: Point; var unzoomed: Rect);
- begin
- AssertValidWindow( window );
- SetPort(window);
- if zoomout then begin
- GetWindowRect(window, unzoomed);
- ZoomWindowOut(window, document_titlebar_height, idealsize);
- end else begin
- SetWindowRect(window, unzoomed);
- end;
- end;
-
- function TitleBarOnScreen (window: WindowPtr): boolean;
- var
- rgn: RgnHandle;
- r: Rect;
- title: RgnHandle;
- begin
- AssertValidWindow( window );
- rgn := NewRgn;
- title := NewRgn;
- CopyRgn(GetWindowStructureRegion(window), rgn);
- GetWindowRect(window, r);
- r.bottom := r.top;
- r.top := r.top - document_titlebar_height;
- InsetRect(r, 2, 2);
- RectRgn(title, r);
- if EmptyRgn( rgn ) then begin
- UnionRgn(rgn, title, rgn);
- end else begin
- SectRgn(rgn, title, rgn);
- end;
- SectRgn(rgn, GetGrayRgn, rgn);
- TitleBarOnScreen := not EmptyRgn(rgn);
- DisposeRgn(title);
- DisposeRgn(rgn);
- end;
-
- function IsWindowShaded(window: WindowPtr): boolean;
- begin
- AssertValidWindow( window );
- IsWindowShaded := EmptyRgn(GetWindowContentRegion(window));
- end;
-
- function FrontWindowList: WindowPtr;
- begin
- FrontWindowList := WindowPtr(LMGetWindowList);
- end;
-
- function NextWindowList( window: WindowPtr ): WindowPtr;
- begin
- AssertValidWindow( window );
- NextWindowList := WindowPtr( WindowPeek(window)^.nextWindow );
- end;
-
- function WindowInWindowList (window: WindowPtr): boolean;
- var
- nw: WindowPtr;
- begin
- AssertValidWindow( window );
- nw := FrontWindowList;
- while (nw <> nil) & (window <> nw) do begin
- nw := NextWindowList( nw );
- end;
- WindowInWindowList := nw <> nil;
- end;
-
- procedure StaggerWindow( window: WindowPtr );
- const
- start_v = 42;
- start_h = 5;
- rel_v = 20;
- rel_h = 20;
- finish_v = 10;
- finish_h = 40;
- max_slots_v = 100;
- max_slots_h = 10;
- var
- mainr, wr: Rect;
- slots_v, slots_h: integer;
- counts: array[ 0..max_slots_v, 0..max_slots_h ] of integer;
- nw: WindowPtr;
- sv, sh: integer;
- off_v, off_h: integer;
- best_v, best_h, best: integer;
- begin
- AssertValidWindow( window );
- GetWindowRect( window, wr );
- mainr := GetMainDevice^^.gdRect;
- slots_v := RectHeight( mainr ) - RectHeight( wr ) - start_v - finish_v;
- slots_v := Min( Choose( slots_v < 0, 0, slots_v div rel_v ), max_slots_v );
- slots_h := RectWidth( mainr ) - RectWidth( wr ) - start_h - finish_h;
- slots_h := Min( Choose( slots_h < 0, 0, slots_h div rel_h ), max_slots_h );
-
- for sv := 0 to slots_v do begin
- for sh := 0 to slots_h do begin
- counts[ sv, sh ] := 0;
- end;
- end;
-
- nw := FrontWindowList;
- while (nw <> nil) do begin
- if nw <> window then begin
- GetWindowRect( nw, wr );
- off_v := wr.top - mainr.top - start_v;
- off_h := wr.left - mainr.left - start_h;
- if (off_v >= 0) & (off_v mod rel_v = 0) & (off_h >= 0) & (off_h mod rel_h = 0) then begin
- off_v := off_v div rel_v;
- off_h := off_h div rel_h;
- if (off_v <= slots_v) & (off_h <= slots_h) then begin
- counts[ off_v, off_h ] := counts[ off_v, off_h ] + 1;
- end;
- end;
- end;
- nw := NextWindowList( nw );
- end;
-
- best := maxint;
- for sh := 0 to slots_h do begin
- for sv := 0 to slots_v do begin
- off_h := (sh + sv) mod (slots_h + 1);
- if counts[ sv, off_h ] < best then begin
- best := counts[ sv, off_h ];
- best_v := sv;
- best_h := off_h;
- end;
- end;
- end;
-
- MoveWindow( window, mainr.left + start_h + best_h*rel_h, mainr.top + start_v + best_v*rel_v, false );
- end;
-
- procedure CenterWindow( window: WindowPtr; center: Rect );
- var
- frame: Rect;
- begin
- GetWindowRect( window, frame );
- CenterRect( frame, center );
- SetWindowRect( window, frame );
- end;
-
- end.